#include <iostream>
#include <vector>
#include <cmath>
#include <string>
#include <fstream>
#include <functional>
#include <Eigen/Dense>
#include "muParser.h"
#include "CubicPPSplines.h"
#include "CubicBSplines.h"

using namespace std;
using namespace Eigen;

void generateKnots(vector<double>& knots, double start, double end, int n) {
    if (n <= 0) {
        throw runtime_error("节点数量必须大于0！");
    }
    knots.resize(n);
    double step = (end - start) / (n - 1);
    for (int i = 0; i < n; i++) {
        knots[i] = start + i * step;
    }
}

bool parseFunction(const string& expression, const vector<double>& knots, vector<double>& f) {
    try {
        mu::Parser parser;
        double x;
        parser.DefineVar("x", &x);
        parser.SetExpr(expression);

        for (size_t i = 0; i < knots.size(); ++i) {
            x = knots[i];
            f[i] = parser.Eval();
        }
        return true;
    } catch (...) {
        cerr << "解析错误：" << endl;
        return false;
    }
}

int main() {
    cout << "请输入节点数量：" << endl;
    int n;
    cin >> n;
    if (n <= 1) {
        cerr << "节点数量必须大于1！" << endl;
        return -1;
    }

    vector<double> knots(n);
    cout << "请输入节点区间（例如 0 和 10）:" << endl;
    double start, end;
    cin >> start >> end;

    if (start >= end) {
        cerr << "无效的区间范围！" << endl;
        return -1;
    }

    generateKnots(knots, start, end, n);

    cout << "请输入函数表达式（变量用x，例如 sin(x) 或 x^2）：";
    string expression;
    cin >> expression;

    vector<double> f(n); // 存储函数值
    if (!parseFunction(expression, knots, f)) {
        return -1;
    }

    cout << "请选择样条拟合类型：" << endl;
    cout << "1. 完全B样条" << endl;
    cout << "2. 自然B样条" << endl;
    cout << "3. 周期B样条" << endl;
    cout << "4. 完全pp样条" << endl;
    cout << "5. 自然pp样条" << endl;
    cout << "6. 周期pp样条" << endl;

    int choice;
    cin >> choice;

    // 文件写入和 gnuplot 脚本创建
    ofstream outFile("spline_data.txt");
    if (!outFile) {
        cerr << "无法打开文件 spline_data.txt" << endl;
        return -1;
    }

    // 生成样条曲线数据
    if (choice == 1) {
        double m_1, m_n;
        cout << "请输入边界条件 m_1 和 m_n（以空格分隔）：";
        cin >> m_1 >> m_n;

        CubicBSpline_complete spline(knots, f, m_1, m_n);

        for (int i = 0; i < 200; ++i) {
            double xi = start + (end - start) * i / 199;
            double yi = spline.B_value(xi); // B 样条使用 B_value
            outFile << xi << " " << yi << " 0" << endl; // 第三列标识为样条曲线
        }
    } else if (choice == 2) {
        CubicBSpline_natural spline(knots, f);

        for (int i = 0; i < 200; ++i) {
            double xi = start + (end - start) * i / 199;
            double yi = spline.B_value(xi); // B 样条使用 B_value
            outFile << xi << " " << yi << " 0" << endl; // 第三列标识为样条曲线
        }
    } else if (choice == 3) {
        if (fabs(f.front() - f.back()) > 1e-10) {
            cerr << "周期样条要求首尾函数值相等！" << endl;
            return -1;
        }

        CubicBSpline_periodic spline(knots, f);

        for (int i = 0; i < 200; ++i) {
            double xi = start + (end - start) * i / 199;
            double yi = spline.B_value(xi); // B 样条使用 B_value
            outFile << xi << " " << yi << " 0" << endl; // 第三列标识为样条曲线
        }
    } else if (choice == 4) {
        double m_1, m_n;
        cout << "请输入边界条件 m_1 和 m_n（以空格分隔）：";
        cin >> m_1 >> m_n;

        CubicPPSplinescomplete spline(knots, f, m_1, m_n);

        for (int i = 0; i < 200; ++i) {
            double xi = start + (end - start) * i / 199;
            double yi = spline.value(xi); // PP 样条使用 value
            outFile << xi << " " << yi << " 0" << endl; // 第三列标识为样条曲线
        }
    } else if (choice == 5) {
        CubicPPSplinesnatural spline(knots, f, 0, 0); // 默认边界条件为 0

        for (int i = 0; i < 200; ++i) {
            double xi = start + (end - start) * i / 199;
            double yi = spline.value(xi); // PP 样条使用 value
            outFile << xi << " " << yi << " 0" << endl; // 第三列标识为样条曲线
        }
    } else if (choice == 6) {
        if (fabs(f.front() - f.back()) > 1e-10) {
            cerr << "周期样条要求首尾函数值相等！" << endl;
            return -1;
        }

        CubicPPSplinesperiodic spline(knots, f);

        for (int i = 0; i < 200; ++i) {
            double xi = start + (end - start) * i / 199;
            double yi = spline.value(xi); // PP 样条使用 value
            outFile << xi << " " << yi << " 0" << endl; // 第三列标识为样条曲线
        }
    } else {
        cerr << "无效的选择！" << endl;
        return -1;
    }

    // 生成原函数数据
    mu::Parser parser;
    double x;
    parser.DefineVar("x", &x);
    parser.SetExpr(expression);

    for (int i = 0; i < 200; ++i) {
        double xi = start + (end - start) * i / 199;
        x = xi;
        double yi = parser.Eval();
        outFile << xi << " " << yi << " 1" << endl; // 第三列标识为原函数
    }

    outFile.close();

    // 创建 gnuplot 脚本
    ofstream scriptFile("plot_splines.gp");
    if (!scriptFile) {
        cerr << "无法创建 gnuplot 脚本文件" << endl;
        return -1;
    }

    scriptFile << "set terminal pngcairo enhanced color size 1200,800\n";
    scriptFile << "set output 'curvefitting.png'\n";
    scriptFile << "set title 'Curve Fitting'\n";
    scriptFile << "set xlabel 'x'\n";
    scriptFile << "set ylabel 'y'\n";
    scriptFile << "set key outside\n";
    scriptFile << "plot '< grep \" 0$\" spline_data.txt' using 1:2 with lines title 'Curve Fitting' lc rgb 'blue', \\\n";
    scriptFile << "     '< grep \" 1$\" spline_data.txt' using 1:2 with lines title 'Original Function' lc rgb 'red' lt 2\n";
    scriptFile.close();

    // 运行 gnuplot 脚本
    system("gnuplot plot_splines.gp");
    cout << "Plot saved as 'curvefitting.png'" << endl;

    return 0;
}